package cc.leap.service;

import cc.leap.constant.CacheConstants;
import cc.leap.constant.Constants;
import cc.leap.constant.UserConstants;
import cc.leap.exception.UserLoginException;
import cc.leap.exception.UserPasswordNotMatchException;
import cc.leap.exception.UserPasswordRetryLimitExceedException;
import cc.leap.helper.LoginHelper;
import cc.leap.helper.TenantHelper;
import cc.leap.mapper.SysMenuMapper;
import cc.leap.mapper.SysRoleMapper;
import cc.leap.mapper.SysUserMapper;
import cc.leap.model.LoginUser;
import cc.leap.model.domain.SysRole;
import cc.leap.model.domain.SysUser;
import cc.leap.thread.AsyncManager;
import cc.leap.utils.AddressUtils;
import cc.leap.utils.CacheUtils;
import cc.leap.utils.IpUtils;
import cc.leap.utils.MessageUtils;
import cc.leap.utils.RsaUtils;
import cc.leap.utils.StringUtils;
import cc.leap.web.vo.LoginBody;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.util.ObjectUtil;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.collections4.CollectionUtils;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.handle.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * SysLoginService
 *
 * @author leap
 * @date 2024/8/15
 */
@Component
public class SysLoginService {
    protected final Logger log = LoggerFactory.getLogger(this.getClass());

    @Inject
    SysUserMapper sysUserMapper;

    @Inject
    SysRoleMapper sysRoleMapper;

    @Inject
    SysMenuMapper sysMenuMapper;

    @Inject(value = "${user.password.maxRetryCount}")
    private int maxRetryCount;

    @Inject(value = "${user.password.lockTime}")
    private int lockTime;

    public String login(LoginBody loginBody) {

        if (StringUtils.isEmpty(loginBody.getUsername()) || StringUtils.isEmpty(loginBody.getPassword())) {
            AsyncManager.executeRecord(loginBody.getUsername(), Constants.LOGIN_FAIL, MessageUtils.message("not.null"));
            throw new UserLoginException();
        }
        SysUser sysUser = sysUserMapper.selectByUsername(loginBody.getUsername());

        try {
            loginBody.setPassword(RsaUtils.decryptByPrivateKey(loginBody.getPassword()));
        } catch (Exception e) {
            log.error("密码解密失败！");
        }
        validate(loginBody.getPassword(), sysUser);
        AsyncManager.executeRecord(loginBody.getUsername()
                , Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));

        StpUtil.login(sysUser.getUserId());
        String token = StpUtil.getTokenValue();

        LoginUser loginUser = new LoginUser(sysUser.getUserId(), sysUser.getDeptId(), sysUser
                , getMenuPermission(sysUser), getRolePermission(sysUser));
        loginUser.setToken(token);
        setUserAgent(loginUser);
        loginUser.setLoginTime(System.currentTimeMillis());
        LoginHelper.set(loginUser);
        CacheUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + ":" + token, loginUser);
        return token;
    }

    private String getCacheKey(String username) {
        return CacheConstants.PWD_ERR_CNT_KEY + username;
    }

    public void validate(String password, SysUser user) {
        String username = user.getUserName();
        Integer retryCount = CacheUtils.getCacheObject(getCacheKey(username), Integer.class);

        if (retryCount == null) {
            retryCount = 0;
        }

        if (retryCount >= maxRetryCount) {
            AsyncManager.executeRecord(username, Constants.LOGIN_FAIL,
                    MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime));
            throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
        }

        if (!LoginHelper.matchesPassword(password, user.getPassword())) {
            retryCount = retryCount + 1;
            AsyncManager.executeRecord(username, Constants.LOGIN_FAIL,
                    MessageUtils.message("user.password.retry.limit.count", retryCount));
            CacheUtils.setCacheObject(getCacheKey(username), retryCount, lockTime * 60);
            throw new UserPasswordNotMatchException();
        } else {
            CacheUtils.deleteObject(getCacheKey(username));
        }
    }



    public void logout() {
        try {
            LoginUser loginUser = LoginHelper.getN();
            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
                // 超级管理员 登出清除动态租户
                TenantHelper.clearDynamic();
            }
            if (ObjectUtil.isNotNull(loginUser)) {
                String userName = loginUser.getUsername();
                CacheUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + ":" + loginUser.getToken());
                // 记录用户退出日志
                AsyncManager.executeRecord(userName, Constants.LOGOUT, "退出成功");
            }
        } catch (Exception e) {
            log.error("登出失败！", e);
        } finally {
            StpUtil.logout();
        }
    }

    public Set<String> getRolePermission(SysUser user) {
        Set<String> permsSet = new HashSet<>();
        if (LoginHelper.isSuperAdmin(user.getUserId())) {
            permsSet.add(UserConstants.SUPER_ADMIN);
        } else {
            List<SysRole> perms = sysRoleMapper.selectRolePermissionByUserId(user.getUserId());
            for (SysRole perm : perms) {
                if (ObjectUtil.isNotNull(perm)) {
                    permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(",")));
                }
            }
        }
        return permsSet;
    }

    public Set<String> getMenuPermission(SysUser user) {
        Set<String> perms = new HashSet<>();
        // 管理员拥有所有权限
        if (LoginHelper.isSuperAdmin(user.getUserId())) {
            perms.add("*");
        } else {
            List<SysRole> roles = user.getRoles();
            if (!CollectionUtils.isEmpty(roles)) {
                // 多角色设置permissions属性，以便数据权限匹配权限
                for (SysRole role : roles) {
                    Set<String> rolePerms = sysMenuMapper.selectMenuPermsByRoleId(role.getRoleId()).stream().collect(Collectors.toSet());
                    role.setPermissions(rolePerms);
                    perms.addAll(rolePerms);
                }
            } else {
                perms.addAll(sysMenuMapper.selectMenuPermsByUserId(user.getUserId()));
            }
        }
        return perms;
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(LoginUser loginUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(Context.current().header("User-Agent"));
        String ip = IpUtils.getIpAddr();
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
    }
}
